package com.lazi.june.gateway.error;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lazi.june.common.base.Result;
import com.lazi.june.common.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 全局异常处理器
 * <p>
 * 该类用于统一处理网关中发生的所有异常，将异常信息转换为标准的 JSON 格式响应。
 * 实现了 Spring WebFlux 的 ErrorWebExceptionHandler 接口，用于响应式 Web 应用的异常处理。
 * <p>
 * 主要功能：
 * 1. 捕获网关运行过程中的所有异常（认证失败、限流、路由错误等）
 * 2. 将异常信息转换为统一的 JSON 响应格式
 * 3. 记录详细的错误日志，便于问题排查
 * 4. 设置合适的 HTTP 状态码
 * <p>
 * 异常处理优先级：
 * - @Order(Ordered.HIGHEST_PRECEDENCE) 确保此处理器最先执行
 * - 可以捕获所有过滤器、路由过程中抛出的异常
 * <p>
 * 响应格式示例（使用 Result 对象）：
 * <pre>
 * {
 *   "code": 401,
 *   "message": "Unauthorized",
 *   "data": {
 *     "path": "/api/users",
 *     "timestamp": "2025-01-15T10:30:45.123+08:00"
 *   }
 * }
 * </pre>
 * <p>
 * 支持的异常类型：
 * - ResponseStatusException: 自定义状态码异常（会提取状态码）
 * - 其他异常: 统一返回 500 Internal Server Error
 *
 * @author huliqian
 * @since 2025/8/21
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class GlobalErrorHandler implements ErrorWebExceptionHandler {

    /**
     * JSON 序列化工具
     * 用于将错误信息对象转换为 JSON 字符串
     */
    private final ObjectMapper mapper = JsonUtil.getObjectMapper();

    /**
     * 处理异常并返回统一格式的错误响应（使用 Result 对象）
     * <p>
     * 处理流程：
     * 1. 判断异常类型，确定 HTTP 状态码
     * 2. 记录错误日志（包含请求方法、路径、状态码、错误信息）
     * 3. 构建 Result 错误响应对象（包含 code、message、data）
     * 4. 序列化为 JSON 并写入响应
     * 5. 如果序列化失败，返回简化的错误信息
     *
     * @param exchange 当前请求的 ServerWebExchange 对象
     * @param ex       捕获到的异常对象
     * @return Mono<Void> 异步响应
     */
    @NonNull
    @Override
    public Mono<Void> handle(@NonNull ServerWebExchange exchange, @NonNull Throwable ex) {
        // 1. 确定 HTTP 状态码
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        if (ex instanceof ResponseStatusException rse) {
            // 如果是 ResponseStatusException，使用其携带的状态码
            status = Optional.ofNullable(HttpStatus.resolve(rse.getStatusCode().value()))
                    .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
        }

        // 2. 获取请求路径
        String path = exchange.getRequest().getURI().getRawPath();

        // 3. 记录错误日志（格式：[GW-ERROR] GET /api/users -> 401 : Unauthorized）
        log.warn("[GW-ERROR] {} {} -> {} : {}",
                exchange.getRequest().getMethod(),
                path,
                status.value(),
                ex.getMessage(), 
                ex);

        // 4. 构建 Result 错误响应对象
        String errorMessage = Optional.ofNullable(ex.getMessage())
                .orElse(status.getReasonPhrase());

        // 构建额外的错误详情（路径和时间戳）
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("path", path);
        errorDetails.put("timestamp", OffsetDateTime.now().toString());

        // 创建 Result 对象
        Result<Map<String, Object>> result = Result.error(status.value(), errorMessage);
        // 如果需要，可以通过创建包含 data 的新 Result 对象
        Result<Map<String, Object>> resultWithDetails = new Result<>(
                result.getCode(),
                result.getMessage(),
                errorDetails
        );

        // 5. 序列化为 JSON
        byte[] bytes;
        try {
            bytes = mapper.writeValueAsBytes(resultWithDetails);
        } catch (Exception e) {
            // 如果序列化失败，返回简化的 Result JSON
            Result<?> fallbackResult = Result.error(status.value(), status.getReasonPhrase());
            try {
                bytes = mapper.writeValueAsBytes(fallbackResult);
            } catch (Exception ex2) {
                // 最后的降级方案：手动构造 JSON
                bytes = ("{\"code\":" + status.value() + ",\"message\":\"" + status.getReasonPhrase() + "\"}").getBytes(StandardCharsets.UTF_8);
            }
        }

        // 6. 设置响应头和状态码
        exchange.getResponse().setStatusCode(status);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 7. 写入响应体
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(bytes)));
    }
}